
#ifndef _TTG_PIPELINE_PARAMETRIZEDOUTPUT_
#define _TTG_PIPELINE_PARAMETRIZEDOUTPUT_

#include "NavigatorNode.h"
#include "ParametrizedInput.h"
#include "../ContextMap.h"

namespace ttg_internal
{
	namespace pipeline
	{
		//Internal auxiliary class, that is used for grouping received task data
		//and storing produced wise tasks.
		class DataSet
		{
			private:
				ttg::structures::UniversalStack *buffer;
				std::vector<ttg::services::WiseTask *> produced_tasks;
				ttg::pipeline::ContextID context_id;
			public:
				//Creates data set for given buffer.
				//Note: buffer will changed, by not released.
				inline DataSet(ttg::structures::UniversalStack *buffer,
							   ttg::pipeline::ContextID context_id)
				{
#ifdef TTG_DEBUG
					if (buffer == NULL)
						throw ttg::Error(ttg::Error::FATAL, "DataSet", "DataSet",
										 "Data buffer cannot be equal to NULL");
#endif
					this->buffer = buffer;
					this->context_id = context_id;
				}
				//Returns buffer with received data.
				inline ttg::structures::UniversalStack &getBuffer()
				{ return *buffer; }
				//Adds produced task.
				inline void addTask(ttg::services::WiseTask *task)
				{ produced_tasks.push_back(task); }
				//Adds produced tasks.
				inline void addTasks(std::vector<ttg::services::WiseTask *> &tasks)
				{ produced_tasks.insert(produced_tasks.end(), tasks.begin(), tasks.end()); }
				//Returns all produced tasks.
				inline std::vector<ttg::services::WiseTask *> &getTasks()
				{ return produced_tasks; }
				//Returns ID of current context.
				inline ttg::pipeline::ContextID getContextID()
				{ return context_id; }
		};

		//Definition of used class.
		template <class T>
		class ParametrizedInput;

		//Template class, which represents abstract output (next node, which can receive TaskData).
		template <class T>
		class ParametrizedOutput
		{
			private:
				NavigatorNode *navigator;
				ttg::structures::Loopback<DataSet *> on_data_receive;
				std::vector<ParametrizedInput<T> *> inputs;
			protected:
				//Assigns navigator.
				inline void setNavigatorForOutput(NavigatorNode *navigator)
				{ this->navigator = navigator; }
				//Returns event for data packing.
				//All produced tasks (that are ready for execution) will be
				//added to the argument via DataSet::addTask().
				//Note: data is placed in buffers of current thread.
				inline ttg::structures::Loopback<DataSet *> &onDataReceive()
				{ return on_data_receive; }
			public:
				inline ParametrizedOutput()
				{ this->navigator = navigator; }
				//Returns assigned navigator.
				inline NavigatorNode *getNavigatorForOutput()
				{ return navigator; }
				//Processes given data and [optionally] produces new task data.
				//Attachs parametrized input.
				inline void attachInput(ParametrizedInput<T> *input)
				{ getParametrizedInputs().push_back(input); }
				//Returns inputs, that was connected to this node/output.
				inline std::vector<ParametrizedInput<T> *> &getParametrizedInputs()
				{ return inputs; }
				//Wrapper, that packs buffer into tasks.
				//Note: buffer will be changed!
				inline std::vector<ttg::services::WiseTask *> packBuffer(ttg::structures::UniversalStack &buffer,
																		 ttg::pipeline::ContextID ID)
				{
					DataSet data_set(&buffer, ID);
					onDataReceive().notify(&data_set);
					return data_set.getTasks();
				}
				//Wrapper, that packs data into tasks (via buffer).
				//Note: this method should be used only by pipeline!
				inline std::vector<ttg::services::WiseTask *> packData(ttg::structures::Enumerator<T> &en,
																	   ttg::pipeline::ContextID ID)
				{
					en.reset();
					//Creating buffer with required data.
					ttg::structures::UniversalStack stack;
					while (en.moveNext())
						stack.push(en.current());
					//Packing.
					return packBuffer(stack, ID);
				}
				virtual ~ParametrizedOutput()
				{ /*nothing*/ }
		};
	}
}

#endif
